package org.water.base.support;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.lang.reflect.Array;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaBuilder.In;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;

/**
 * bean 查询
 * @author qzy
 *
 */
public class BeanQuery<T> {

	public BeanQuery(Class<T> clazz,EntityManager em) {
	   this.em=em;	
	   this.clazz=clazz;
	   criteriaBuilder=this.em.getCriteriaBuilder();
	   criteriaQuery=criteriaBuilder.createQuery(clazz);
	   this.root=criteriaQuery.from(this.clazz);
	}
	
	private Class<T> clazz;
	private EntityManager em;
	private CriteriaBuilder criteriaBuilder;
	private ArrayList<Predicate> predicateList=new ArrayList<Predicate>();
	private ArrayList<Order> orderList=new ArrayList<Order>();
	private CriteriaQuery<T> criteriaQuery;
	private Root<T> root;
	
	/**
	 * 是否去重
	 * @param f
	 * @return
	 */
	public BeanQuery<T> distinct(boolean f)
	{
		criteriaQuery.distinct(f);
		return this;
	}
	/**
	 * 判断一个对象是否为空。它支持如下对象类型：
	 * <ul>
	 * <li>null : 一定为空
	 * <li>字符串 : ""为空,多个空格也为空
	 * <li>数组
	 * <li>集合
	 * <li>Map
	 * <li>其他对象 : 一定不为空
	 * </ul>
	 * 
	 * @param obj
	 *            任意对象
	 * @return 是否为空
	 */
	public final static boolean isEmpty(final Object obj) {
		if (obj == null) {
			return true;
		}
		if (obj instanceof String) {
			return "".equals(String.valueOf(obj).trim());
		}
		if (obj.getClass().isArray()) {
			return Array.getLength(obj) == 0;
		}
		if (obj instanceof Collection<?>) {
			return ((Collection<?>) obj).isEmpty();
		}
		if (obj instanceof Map<?, ?>) {
			return ((Map<?, ?>) obj).isEmpty();
		}
		return false;
	}

	public final static boolean isNotEmpty(final Object obj) {
		return !isEmpty(obj);
	}
	/**
	 * 等于
	 * @param field  bean中对应的字段
	 * @param value  值 
	 * @return
	 */
	public BeanQuery<T> eq(String field,Object value)
	{
		
		predicateList.add(criteriaBuilder.equal(root.get(field), value));
		return this;
	}
	/**
	 * 设置查询字段 filed为bean中的属性,如 "id","name",使用该字段需要在bean中设置对应查询字段的构造函数
	 * arc(id,name)
	 * @param fileds
	 * @return
	 */
	public BeanQuery<T> select(String ...fileds)
	{
		ArrayList<Selection<?>> selectFields=new ArrayList<Selection<?>>();
		for(String field:fileds)
		{
			Selection<Object> selection=root.get(field);
			selection.alias(field);
			selectFields.add(selection);
		}
		if(!selectFields.isEmpty()) criteriaQuery.multiselect(selectFields);
		return this;
	}
	/**
	 * 或者查询  多个属性加括号并行查询 等于
	 * @param propertyNames 多个属性
	 * @param value
	 * @return
	 */
	public BeanQuery<T> or(String[] propertyNames, Object value) {
		if (isEmpty(value))
			return this;
		if ((propertyNames == null) || (propertyNames.length == 0))
		    return this;

		Predicate predicate = criteriaBuilder.or(criteriaBuilder.equal(root.get(propertyNames[0]), value));
		for (int i = 1; i < propertyNames.length; ++i)
		{
			predicate = criteriaBuilder.or(predicate, criteriaBuilder.equal(root.get(propertyNames[i]), value));
		    predicateList.add(predicate);
		}
		return this;
	}
	/**
	 * 或者查询  多个属性加括号并行查询  like
	 * @param propertyNames 多个属性
	 * @param value
	 * @return
	 */
	public BeanQuery<T> orLike(String[] propertyNames, String value) {
		if (isEmpty(value))
			return this;
		if ((propertyNames == null) || (propertyNames.length == 0))
		    return this;
		
		Predicate predicate = criteriaBuilder.or(criteriaBuilder.like(root.get(propertyNames[0]).as(String.class), value));
		for (int i = 1; i < propertyNames.length; ++i)
		{
			predicate = criteriaBuilder.or(predicate, criteriaBuilder.like(root.get(propertyNames[i]).as(String.class), value));
		    predicateList.add(predicate);
		}
		return this;
	}
	
	/** 空 */
	public BeanQuery<T> isNull(String propertyName) {
		predicateList.add(criteriaBuilder.isNull(root.get(propertyName)));
		return this;
	}

	/** 非空 */
	public BeanQuery<T> isNotNull(String propertyName) {
		predicateList.add(criteriaBuilder.isNotNull(root.get(propertyName)));
		return this;
	}

	/** 不相等 */
	public BeanQuery<T> notEq(String propertyName, Object value) {
		if (isEmpty(value)) {
			return this;
		}
		predicateList.add(criteriaBuilder.notEqual(root.get(propertyName), value));
		return this;
	}

	/**
	 * not in
	 * 
	 * @param propertyName
	 *            属性名称
	 * @param value
	 *            值集合
	 */
	public BeanQuery<T> notIn(String propertyName, Collection value) {
		if ((value == null) || (value.size() == 0)) {
			return this;
		}
		Iterator iterator = value.iterator();
		In in = criteriaBuilder.in(root.get(propertyName));
		while (iterator.hasNext()) {
			in.value(iterator.next());
		}
		predicateList.add(criteriaBuilder.not(in));
		return this;
	}

	/**
	 * 模糊匹配
	 * 
	 * @param propertyName
	 *            属性名称
	 * @param value
	 *            属性值
	 */
	public BeanQuery<T> like(String propertyName, String value) {
		if (isEmpty(value))
			return this;
		if (value.indexOf("%") < 0) value = "%" + value + "%";
		predicateList.add(criteriaBuilder.like(root.get(propertyName).as(String.class), value));
		return this;
	}

	/**
	 * 时间区间查询
	 * 
	 * @param propertyName
	 *            属性名称
	 * @param lo
	 *            属性起始值
	 * @param go
	 *            属性结束值
	 */
	public BeanQuery<T> between(String propertyName, Date lo, Date go) {
		if (isNotEmpty(lo) && isNotEmpty(go)) {
			
			predicateList.add(criteriaBuilder.between(root.<Date> get(propertyName), lo, go));
			return this;
		}

		// SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		if (isNotEmpty(lo) && isEmpty(go)) {
			predicateList.add(criteriaBuilder.lessThan(root.<Date> get(propertyName), lo));// formatter.format(lo)
			return this;
		}

		if (isNotEmpty(go)) {
			predicateList.add(criteriaBuilder.greaterThan(root.<Date> get(propertyName), go));
		}
		return this;

	}

	public BeanQuery<T> between(String propertyName, Number lo, Number go) {
		if (!(isEmpty(lo)))
			ge(propertyName, lo);

		if (!(isEmpty(go)))
			le(propertyName, go);
		return this;
	}

	/**
	 * 小于等于
	 * 
	 * @param propertyName
	 *            属性名称
	 * @param value
	 *            属性值
	 */
	public BeanQuery<T> le(String propertyName, Number value) {
		if (isEmpty(value)) {
			return this;
		}
		predicateList.add(criteriaBuilder.le(root.<Number> get(propertyName), value));
		return this;
	}

	/**
	 * 小于
	 * 
	 * @param propertyName
	 *            属性名称
	 * @param value
	 *            属性值
	 */
	public BeanQuery<T> lt(String propertyName, Number value) {
		if (isEmpty(value)) {
			return this;
		}
		predicateList.add(criteriaBuilder.lt(root.<Number> get(propertyName), value));
		return this;
	}

	/**
	 * 大于等于
	 * 
	 * @param propertyName
	 *            属性名称
	 * @param value
	 *            属性值
	 */
	public BeanQuery<T> ge(String propertyName, Number value) {
		if (isEmpty(value)) {
			return this;
		}
		predicateList.add(criteriaBuilder.ge(root.<Number> get(propertyName), value));
		return this;
	}

	/**
	 * 大于
	 * 
	 * @param propertyName
	 *            属性名称
	 * @param value
	 *            属性值
	 */
	public BeanQuery<T> gt(String propertyName, Number value) {
		if (isEmpty(value)) {
			return this;
		}
		predicateList.add(criteriaBuilder.gt(root.<Number> get(propertyName), value));
		return this;
	}

	/**
	 * in
	 * 
	 * @param propertyName
	 *            属性名称
	 * @param value
	 *            值集合
	 */
	public BeanQuery<T> in(String propertyName, Collection value) {
		if ((value == null) || (value.size() == 0)) {
			return this;
		}
		Iterator iterator = value.iterator();
		In in = criteriaBuilder.in(root.get(propertyName));
		while (iterator.hasNext()) {
			in.value(iterator.next());
		}
		predicateList.add(in);
		
		return this;
	}
	/**
	 * 升序
	 * @param fields
	 * @return
	 */
	public BeanQuery<T> orderAsc(String ...fields)
	{
		if (fields == null) return this;
		for(String field:fields)
		{
			orderList.add(this.criteriaBuilder.asc(root.get(field)));
		}
		return this;
	}
	/**
	 * 降序
	 * @param fields
	 * @return
	 */
	public BeanQuery<T> orderDesc(String ...fields)
	{
		if (fields == null) return this;
		for(String field:fields)
		{
			orderList.add(this.criteriaBuilder.desc(root.get(field)));
		}
		return this;
	}
	/**
	 * 返回结果
	 * @return
	 */
	public List<T> list()
	{
		setQueryCondition();
		TypedQuery<T> typedQuery = em.createQuery(criteriaQuery);
		List<T> result = typedQuery.getResultList();
		
		return result;
	}
	
	/**
	 * 返回结果
	 * @param start 开始位置，以0开始
	 * @param pageSize 每页显示数
	 * @return
	 */
	public List<T> list(Integer start,Integer pageSize)
	{
		setQueryCondition();
		TypedQuery<T> typedQuery = em.createQuery(criteriaQuery);
		typedQuery.setFirstResult(start);
		typedQuery.setMaxResults(pageSize);
		List<T> result = typedQuery.getResultList();
		return result;
	}
	/**
	 * 分页查询
	 * @param pageIndex
	 * @param pageSize
	 * @return
	 */
	public Page<T> page(Integer pageIndex,Integer pageSize)
	{
		setQueryCondition();
		TypedQuery<T> typedQuery = em.createQuery(criteriaQuery);
		typedQuery.setFirstResult(pageIndex*pageSize);
		typedQuery.setMaxResults(pageSize);
		PageRequest pageRequest=new PageRequest(pageIndex, pageSize);
		List<T> result = typedQuery.getResultList();

		return new PageImpl(result, pageRequest, count());
	}
	/**
	 * 查询一条数据
	 * @return
	 */
	public T one()
	{
		T t=null;
		setQueryCondition();
		TypedQuery<T> typedQuery = em.createQuery(criteriaQuery);
		List<T> list=typedQuery.getResultList();
		if(list!=null && list.size()>0)
		{
			t=list.get(0);
		}
		//t = typedQuery.getSingleResult();
		return t;
	}
	/**
	 * 统计记录 获取满足条件的记录数
	 * 
	 * @param query
	 *            统计条件
	 */
	@SuppressWarnings("unchecked")
	public Long count() {
		CriteriaQuery<Long> critQuery = criteriaBuilder.createQuery(Long.class);
		Root<T> croot=critQuery.from(this.clazz);
		String talias=this.clazz.getSimpleName()+"_tc";
		croot.alias(talias);
		root.alias(talias);
		critQuery.select(criteriaBuilder.count(croot));
		Predicate[] predicateArr;
		predicateArr=predicateList.toArray(new Predicate[predicateList.size()]);
		critQuery.where(predicateArr);
		Object countV=em.createQuery(critQuery).getSingleResult();
		Long count = (Long) countV;
		root.alias(null);
		
		return count;
	}
	/**
	 * 设置查询条件 包括条件、排序等
	 */
	private void setQueryCondition()
	{
		criteriaQuery.where(predicateList.toArray(new Predicate[predicateList.size()]));
		criteriaQuery.orderBy(orderList);
	}
}
